package mysql

import (
	"context"
	"database/sql"
	"fmt"
	"gitee.com/hexug/go-tools/ioc"
	"gitee.com/hexug/go-tools/ioc/log"
	_ "gitee.com/hexug/go-tools/ioc/log" // 需要保证log一定提前注册
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"strings"
	"time"
)

func init() {
	ioc.Container.RegisterConf(NewMysql())
}

const ConfName = "mysql"

func (m *Mysql) Init() error {
	m.log = ioc.Container.GetConf(log.ConfName).(*log.Log)
	return nil
}

func (m *Mysql) Name() string {
	return ConfName
}

func (m *Mysql) GetConnPool(ctx context.Context) (*sql.DB, error) {
	// 创建dsn
	// multiStatements 让db 可以执行多个语句 select; insert;
	dsn := fmt.Sprintf(
		"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%s&loc=Local&multiStatements=%s",
		m.User,
		m.Password,
		m.Host,
		m.Port,
		m.Database,
		m.Charset,
		m.ParseTime,
		m.MultiStateMents,
	)
	// 创建原生db链接
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, err
	}

	// 对连接池进行设置
	db.SetMaxOpenConns(m.MaxOpenConn) // 最大连接数
	db.SetMaxIdleConns(m.MaxIdleConn) // 最大空闲数
	// 设置可以重复使用连接的最大时间，过期的连接可以在重用之前延迟关闭。
	if m.ConnMaxLifeTime > 0 {
		db.SetConnMaxLifetime(time.Second * time.Duration(m.ConnMaxLifeTime)) // 如果设置的时间小于等于0，则连接不会因连接老化而关闭。
	}
	// 设置连接可能空闲的最长时间。过期的连接可以在重新使用之前延迟关闭。
	if m.ConnMaxIdleTime > 0 {
		db.SetConnMaxIdleTime(time.Second * time.Duration(m.ConnMaxIdleTime)) // 如果设置的时间小于等于0，则不会因为连接在空闲时间就关闭。
	}
	c, cancel := context.WithTimeout(ctx, time.Duration(m.TimeOut)*time.Second)
	defer cancel()
	// 先ping一下数据库，看下是否存活
	if err := db.PingContext(c); err != nil {
		return nil, err
	}
	return db, nil
}

func (m *Mysql) ORM(ctx context.Context) *gorm.DB {
	m.lock.Lock()
	defer m.lock.Unlock()
	if m.db == nil {
		db, err := m.GetConnPool(ctx)
		if err != nil {
			panic(err)
		}
		// 定义时间精度的变量
		var datetimePrecision = 2
		gormdb, err := gorm.Open(
			mysql.New(
				mysql.Config{
					Conn:                     db,
					DefaultDatetimePrecision: &datetimePrecision, // 默认时间精度
				},
			), &gorm.Config{
				// 执行任何 SQL 时都创建并缓存预编译语句，可以提高后续的调用速度
				PrepareStmt: true,

				// 对于写操作（创建、更新、删除），为了确保数据的完整性，GORM 会将它们封装在事务内运行。
				// 但这会降低性能，如果没有这方面的要求，您可以在初始化时禁用它，这将获得大约 30%+ 性能提升
				SkipDefaultTransaction: true,

				// 要有效地插入大量记录，请将一个 slice 传递给 Create 方法、可以批量创建
				// CreateBatchSize: 200,
			},
		)
		if err != nil {
			panic(err)
		}
		m.db = gormdb
		if strings.ToLower(m.log.Level) == "debug" {
			m.db = m.db.Debug()
		}
	}
	return m.db
}
